 //
// Created by Jinyu Zhu on 2022/4/17.
//

#include "graph.h"
#include "sstream"

void UndirectedGraph::Node::Save(FOut& fOut) const {
    fOut.Save(Id); fOut.Save(Name); fOut.Save(Value);
    fOut.Save(NIdVec);
}

UndirectedGraph::Node::Node(FIn &fIn) : NIdVec(), Id(0), Name(), Value(0) {
    fIn.Load(Id); Name = fIn.LoadStr(); fIn.Load(Value); fIn.Load(NIdVec);
}

int UndirectedGraph::Node::DelNbr(const int &NId) {
    auto iter = binary_find(NIdVec.begin(), NIdVec.end(), NId);
    if (iter != NIdVec.end()) {
        NIdVec.erase(iter); return NId;
    } else { return -1; }
}

int UndirectedGraph::AddNode(const int& Id, const std::string& Name, const double& Value) {
    if (NodeMap.find(Id) == NodeMap.end() && NodeIndex.find(Name) == NodeIndex.end()) {
        NodeMap.insert({Id, Node(Id, Name, Value)});
        NodeIndex.insert({Name, Id});
        return Id;
    } else {
        return -1;
    }
}

int UndirectedGraph::AddNode(const std::string &Name, const double& Value) {
    if (NodeIndex.find(Name) == NodeIndex.end()) {
        int NewNId = MaxNId; MaxNId++;
        AddNode(NewNId, Name, Value);
        return NewNId;
    } else {
        return -1;
    }
}

int UndirectedGraph::AddLogNode(const std::string& fileName, const int &LineNo, const double& Value) {
    std::string Name = ParseLogNName(fileName, LineNo);
    return AddNode(Name);
}

int UndirectedGraph::AddTagNode(const std::string &TagKey, const std::string &TagValue, const double& Value) {
    std::string Name = ParseTagNName(TagKey, TagValue);
    return AddNode(Name);
}

int UndirectedGraph::AddEdge(const int& SrcNId, const int& DstNId) {
    if (IsNode(SrcNId) && IsNode(DstNId)) {
        Node& SrcNode = GetNode(SrcNId);
        Node& DstNode = GetNode(DstNId);
        if (!SrcNode.SearchBinSorted(DstNId)) {
            SrcNode.InsNIdVecSorted(DstNId);
            DstNode.InsNIdVecSorted(SrcNId);
            NEdges++;
            return NEdges;
        } else { return -1; }
    } else {
        return -1;
    }
}

int UndirectedGraph::AddEdge(const std::string &LogFileName, const int &LineNo,
                              const std::string &TagKey, const std::string &TagValue) {
    if (IsLogNode(LogFileName, LineNo) && IsTagNode(TagKey, TagValue)) {
        Node& SrcNode = GetLogNode(LogFileName, LineNo);
        Node& DstNode = GetTagNode(TagKey, TagValue);
        if (!SrcNode.SearchBinSorted(DstNode.Id)) {
            SrcNode.InsNIdVecSorted(DstNode.Id);
            DstNode.InsNIdVecSorted(SrcNode.Id);
            NEdges++;
            return NEdges;
        } else { return -1; }
    } else {
        return -1;
    }
}

bool UndirectedGraph::IsEdge(const int &SrcId, const int &DstId) {
    if (IsNode(SrcId) && IsNode(DstId)) {
        return GetNode(SrcId).SearchBinSorted(DstId);
    } else return false;
}


int UndirectedGraph::GetTagNodeId(const std::string &TagKey, const std::string &TagValue) {
    auto iter = NodeIndex.find(ParseTagNName(TagKey, TagValue));
    if (iter == NodeIndex.end()) return -1;
    else return iter->second;
}

int UndirectedGraph::GetLogNodeId(const std::string &LogFileName, const int &LineNo) {
    auto iter = NodeIndex.find(ParseLogNName(LogFileName, LineNo));
    if (iter == NodeIndex.end()) return -1;
    else return iter->second;
}

std::string UndirectedGraph::BriefInfo() {
    std::stringstream ss;
    ss << "single-undirected graph: ";
    for (const auto & s: TagKeyVec) {
        ss << s << ", ";
    }
    ss << "\n nodes: " << std::to_string(NodeMap.size()) << " edges: " << std::to_string(NEdges);
    return ss.str();
}

int UndirectedGraph::DelNode(const int &NId) {
    if (!IsNode(NId)) return -1;
    Node& N = GetNode(NId);
    NEdges -= N.GetDeg();
    for (int e = 0; e < N.GetDeg(); e++) {
        const int NbrId = N.GetNbrNId(e);
        if (NbrId == NId) continue;
        Node& Nbr = GetNode(NbrId);
        AssertR(Nbr.DelNbr(NId) != -1, "trying to delete a node, but its connective node is incomplete");
    }

    NodeIndex.erase(NodeIndex.find(N.Name));
    NodeMap.erase(NodeMap.find(NId));

    return NId;
}

int UndirectedGraph::DelEdge(const int &SrcNId, const int &DstNId) {
    if (!(IsNode(SrcNId) && IsNode(DstNId))) return -1;
    Node& SrcNode = GetNode(SrcNId);
    Node& DstNode = GetNode(DstNId);
    if (!SrcNode.SearchBinSorted(DstNId)) return -1;

    SrcNode.DelNbr(DstNId);
    DstNode.DelNbr(SrcNId);
    return 1;
}

UGNodeI UndirectedGraph::BeginNodeI() {
    UGNodeI BeginNodeI = UGNodeI(NodeMap.begin(), this);
    return { BeginNodeI };
}

UGNodeI UndirectedGraph::EndNodeI() {
    UGNodeI EndNodeI = UGNodeI(NodeMap.end(), this);
    return { EndNodeI };
}

UGNodeI UndirectedGraph::GetNodeNIdI(const int& NId) {
    UGNodeI NodeI = UGNodeI(NodeMap.find(NId), this);
    return { NodeI };
}